/**
 * @file bsp_adc.c
 * @brief ADC封装驱动(仅使用ADC1)
 * @author wangh (wanghuan3037@fiberhome.com)
 * @version 1.0
 * @date 2020-10-17
 * @copyright Copyright (c) 2020  烽火通信
 ***************************************************************
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2019-11-17 <td>1.0     <td>wangh     <td>内容
 * </table>
 */
#include "bsp_adc.h"

#undef LOG_LEVEL_
#define LOG_LEVEL_ 	LOG_LVL_ASSERT
#include "bsp_log.h"

static bool AdcInitialized = false;

#define ADC_TIMEOUT_MAX        0x8000  ///< 约等于8ms

/**
 * @brief ADC初始化
 */
void AdcInit( void )
{
    if( AdcInitialized )
        return;
    
    AdcInitialized = true;

#ifdef STM32F10X
    uint32_t timeOut = ADC_TIMEOUT_MAX;
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE );  /* 使能ADC1通道时钟 */
    RCC_ADCCLKConfig( RCC_PCLK2_Div6 );     /* 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M */

    ADC_InitTypeDef ADC_InitStructure;
    ADC_DeInit(ADC1);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;  /* ADC工作模式:ADC1和ADC2工作在独立模式 */
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;	    /* 模数转换工作在单通道模式 */
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; /* 模数转换工作在单次转换模式 */
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; /* 转换由软件而不是外部触发启动 */
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  /* ADC数据右对齐 */
    ADC_InitStructure.ADC_NbrOfChannel = 1;	            /* 顺序进行规则转换的ADC通道的数目 */
    ADC_Init(ADC1, &ADC_InitStructure);	                /* 根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 */
    #if ADC_TempSensor_EN
    ADC_TempSensorVrefintCmd(ENABLE);                   /* 开启内部温度传感器 */
    #endif

    ADC_Cmd(ADC1, ENABLE);	    /* 使能指定的ADC1 */
    ADC_ResetCalibration(ADC1); /* 使能复位校准 */  
    while(ADC_GetResetCalibrationStatus(ADC1))
    {
        if( ( timeOut-- ) == 0 )
            break;
    } /* 等待复位校准结束 */
    ADC_StartCalibration(ADC1); /* 开启ADC校准 */
    while(ADC_GetCalibrationStatus(ADC1))
    {
        if( ( timeOut-- ) == 0 )
            break;
    } /* 等待校准结束 */

#elif defined GD32F10X
    rcu_periph_clock_enable(RCU_ADC0);	/* 使能ADC通道时钟 */
    rcu_adc_clock_config( RCU_CKADC_CKAPB2_DIV6 );

    adc_deinit( ADC0 );
    adc_mode_config( ADC_MODE_FREE );	/* 所有ADC工作在独立模式 */
    adc_data_alignment_config( ADC0, ADC_DATAALIGN_RIGHT );			/* ADC数据右对齐 LSB */
    adc_special_function_config( ADC0, ADC_SCAN_MODE, DISABLE );	/* 模数转换工作在单通道模式 */
    adc_special_function_config( ADC0, ADC_CONTINUOUS_MODE, DISABLE );	/* 模数转换工作在单次转换模式 */
    adc_external_trigger_source_config( ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE );
    adc_external_trigger_config( ADC0, ADC_REGULAR_CHANNEL, ENABLE );

    adc_channel_length_config( ADC0, ADC_REGULAR_CHANNEL, 1 );
    adc_discontinuous_mode_config( ADC0, ADC_REGULAR_CHANNEL, 1 );	/* 顺序进行规则转换的ADC通道的数目 */

    #if ADC_TempSensor_EN
    adc_tempsensor_vrefint_enable( );	/* 开启内部温度传感器 */
    #endif

    adc_enable( ADC0 );
    vTaskDelay(1);	/* 延迟14个ADCCLK以等待ADC稳定 */
    adc_calibration_enable( ADC0 );	/* ADC校准 */
#endif
}

/**
 * @brief ADC去初始化
 */
void AdcDeInit( void )
{
    AdcInitialized = false;
#ifdef STM32F10X
    ADC_DeInit(ADC1);
#elif defined GD32F10X
    adc_deinit( ADC0 );
#endif
}

/**
 * @brief adc通道初始化
 * @param[in]  obj      adc对象
 * @param[in]  adcInput adc引脚
 * @return int8_t 
 * - @b Sucess : BSP_SUCCESS \n
 * - @b Fail
 * - @ref BSP_ERROR_INVALID_PARAM - 非法入参
 * - @ref BSP_ERROR_ALREADY_INIT - 已初始化，重新初始化先调用 AdcDeInitChannel
 * - @ref BSP_ERROR_NOT_SUPPORTED - 引脚不支持ADC
 */
int8_t AdcInitChannel( Adc_t *obj, PinNames adcInput )
{
    if (obj == NULL)
        return BSP_ERROR_INVALID_PARAM;
    
    if (obj->IsInitialized)
    {
        log_e( "AdcInitChannel error: has already Initialized\r\n" );
        return BSP_ERROR_ALREADY_INIT;
    }

    AdcInit();

    if (adcInput != NC)
    {
        switch (adcInput)
        {
            case PA_0: obj->channel = 0; break;
            case PA_1: obj->channel = 1; break;
            case PA_2: obj->channel = 2; break;
            case PA_3: obj->channel = 3; break;
            case PA_4: obj->channel = 4; break;
            case PA_5: obj->channel = 5; break;
            case PA_6: obj->channel = 6; break;
            case PA_7: obj->channel = 7; break;
            case PB_0: obj->channel = 8; break;
            case PB_1: obj->channel = 9; break;
            case PC_0: obj->channel = 10; break;
            case PC_1: obj->channel = 11; break;
            case PC_2: obj->channel = 12; break;
            case PC_3: obj->channel = 13; break;
            case PC_4: obj->channel = 14; break;
            case PC_5: obj->channel = 15; break;

            default:
                log_e( "AdcInitChannel error: this pin is not support ADC\r\n" );
                return BSP_ERROR_NOT_SUPPORTED;
        }
        
        GpioInit( &obj->AdcPin, adcInput, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 );
        obj->IsInitialized = true;
    }

    return BSP_SUCCESS;
}

/**
 * @brief adc通道去初始化
 * @param  obj          adc对象
 */
void AdcDeInitChannel( Adc_t *obj )
{
    if( obj && obj->IsInitialized )
    {
        obj->IsInitialized = false;
        GpioInit( &obj->AdcPin, obj->AdcPin.pin, PIN_INPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 );    /* 初始化为浮空输入 */
    }
}

/**
 * @brief 读取ADC通道AD值
 * @param  obj          adc对象
 * @return uint16_t AD值
 */
uint16_t AdcReadChannel( Adc_t *obj )
{
    if (obj == NULL)
        return 0;

    if ( obj->IsInitialized == false )
    {
        log_e( "AdcReadChannel error: no Initialized\r\n" );
        return 0;
    }

    uint32_t timeOut = ADC_TIMEOUT_MAX;
#ifdef STM32F10X
    ADC_ClearFlag( ADC1, ADC_FLAG_EOC );
    ADC_RegularChannelConfig( ADC1, obj->channel, 1, ADC_SampleTime_239Cycles5 );   /* ADC通道采样时间为239.5周期 */
    ADC_SoftwareStartConvCmd( ADC1, ENABLE );           /* 使能指定的ADC1的软件转换启动功能 */
    while( RESET == ADC_GetFlagStatus( ADC1, ADC_FLAG_EOC ))
    {
        if( ( timeOut-- ) == 0 )
            break;
    } /* 等待转换结束 */
    return ADC_GetConversionValue(ADC1);                /* 返回最近一次ADC1规则组的转换结果 */

#elif defined GD32F10X
    adc_flag_clear( ADC0, ADC_FLAG_EOC );
    adc_regular_channel_config( ADC0, 0, obj->channel, ADC_SAMPLETIME_239POINT5 );
    adc_software_trigger_enable( ADC0, ADC_REGULAR_CHANNEL );
    while(adc_flag_get( ADC0, ADC_FLAG_EOC ) != SET)
    {
        if( ( timeOut-- ) == 0 )
            break;
    } /* 等待转换结束 */
    return adc_regular_data_read( ADC0 );        		/* 返回最近一次ADC规则组的转换结果 */
#endif
}

/**
 * @brief 均值法ADC采集
 * @param  obj          adc对象
 * @param  times        均值采集次数
 * @return uint16_t AD值
 */
uint16_t AdcReadChannelAverage( Adc_t *obj, uint8_t times )
{
    if (obj == NULL)
        return 0;

    if ( obj->IsInitialized == false )
    {
        log_e( "AdcReadChannelAverage error: no Initialized\r\n" );
        return 0;
    }

    uint32_t temp_val = 0;
    uint8_t t;
    for (t = 0; t < times; t++)
    {
        temp_val += AdcReadChannel( obj );
    }

    return (uint16_t)(temp_val / times);
}

/**
 * @brief 读取ADC通道电压值
 * @param[in]  obj          adc对象
 * @return float 电压值（0~3.3V）
 */
float AdcReadChannelVol( Adc_t *obj )
{
    uint16_t temp_val = AdcReadChannelAverage( obj, 100 );

    return (float)( ((float)(temp_val*3.3))/4096 );
}



#if ADC_TempSensor_EN
/**
 * @brief 获取内部温度传感器温度值
 * @return uint16_t 温度值(扩大了100倍,单位:℃)
 */
float AdcGetTemp( void )
{
#ifdef STM32F10X
    Adc_t adc_temp;
    adc_temp.channel = 16;

    uint16_t t_adc = AdcReadChannelAverage(&adc_temp, 20);

    float temperate = (float)t_adc * (3.3 / 4096);  /* 电压值 */ 
    temperate = (1.43 - temperate) / 0.0043 + 25;   /* 转换为温度值 */

    return temperate;
#elif defined GD32F10X

#endif
}

#endif


/*********** (C) COPYRIGHT 2020 FiberHome *****END OF FILE****/
